//---------------------------------------------------------------------
//  This file is part of the Background Motion solution.
// 
//  Copyright (C) Mindscape (TM).  All rights reserved.
//  http://www.mindscape.co.nz
// 
//  THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//  PARTICULAR PURPOSE.
//---------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
using Mindscape.BackgroundMotion.Website.Infrastructure;

namespace Mindscape.BackgroundMotion.Website.Controls
{
  public class CodeBlock
  {
    private string _code;
    private string _description;
    private string _filename;
    private CodeViewerLineNumbers _lines;

    public CodeBlock(CodeViewerReferenceAttribute descriptor)
    {
      Code = new CodeHighlighter().Highlight(LoadFromFile(descriptor.Filename, descriptor.Lines));
      Description = descriptor.Description;
      Filename = descriptor.Filename;
      Lines = descriptor.Lines;
    }

    private string SystemCodeLocation
    {
      get { return ConfigurationManager.AppSettings["CodeLocation"]; }
    }

    public string Code
    {
      get { return _code; } 
      set { _code = value; }
    }

    public string Description
    {
      get { return _description.Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;"); }
      set { _description = value; }
    }

    public CodeViewerLineNumbers Lines
    {
      get { return _lines; }
      set { _lines = value; }
    }

    public string Filename
    {
      get { return _filename; }
      set { _filename = value; }
    }

    private string LoadFromFile(string filename, CodeViewerLineNumbers codeViewerLineNumbers)
    {
      StringBuilder builder = new StringBuilder();

      using (FileStream fs = File.Open(String.Format("{0}{1}", SystemCodeLocation, filename), FileMode.Open, FileAccess.Read))
      {
        using (StreamReader sr = new StreamReader(fs))
        {
          for (int i = 1; i < codeViewerLineNumbers.From; i++) sr.ReadLine();
          for (int i = 0; i <= codeViewerLineNumbers.To - codeViewerLineNumbers.From; i++)
          {
            builder.AppendLine(sr.ReadLine());
          }

          sr.Close();
        }

        fs.Close();
      }

      return builder.ToString();
    }
  }

  public partial class CodeViewer : UserControl
  {
    private static Dictionary<Type, CodeViewerAttribute> _loadedDeclarations = new Dictionary<Type, CodeViewerAttribute>();
    private static Dictionary<Type, SortedList<int, CodeBlock>> _loadedBlocks = new Dictionary<Type, SortedList<int, CodeBlock>>();   

    protected void Page_Load(object sender, EventArgs e)
    {
      CodeViewerAttribute codeDeclaration = LoadCodeDeclaration();
      SortedList<int, CodeBlock> codeBlocks = LoadCodeBlocks();

      if (codeBlocks.Count == 0) return;

      CodeBlockPlaceholder.Visible = true;
      CodeBlockRepeater.DataSource = codeBlocks;
      CodeBlockRepeater.DataBind();

      if (codeDeclaration != null)
      {
        CodeBlockHeader.Text = codeDeclaration.Heading;
      }
    }

    protected void codeBlockRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
    {
      CodeBlock codeBlock = ((KeyValuePair<int,CodeBlock>)e.Item.DataItem).Value;

      if (codeBlock == null) return;

      Literal code = e.Item.FindControl("code") as Literal;
      Label codeDescription = e.Item.FindControl("codeDescription") as Label;
      Label codeLocation = e.Item.FindControl("codeLocation") as Label;

      if (code == null || codeDescription == null || codeLocation == null) return;

      code.Text = codeBlock.Code;
      codeDescription.Text = codeBlock.Description;
      codeLocation.Text = String.Format("From: {0} (Lines {1} to {2})", codeBlock.Filename, codeBlock.Lines.From, codeBlock.Lines.To);
    }

    private CodeViewerAttribute LoadCodeDeclaration()
    {
      if (!_loadedDeclarations.ContainsKey(Page.GetType()))
      {
        object[] attributes = Page.GetType().GetCustomAttributes(true);

        foreach (Attribute attribute in attributes)
        {
          if (!(attribute is CodeViewerAttribute)) continue;

          _loadedDeclarations[Page.GetType()] = ((CodeViewerAttribute)attribute);
          break;
        }
      }

      return _loadedDeclarations.ContainsKey(Page.GetType()) ? _loadedDeclarations[Page.GetType()] : null;
    }

    private SortedList<int, CodeBlock> LoadCodeBlocks()
    {     
      if (!_loadedBlocks.ContainsKey(Page.GetType()))
      {
        SortedList<int, CodeBlock> codeBlocks = new SortedList<int, CodeBlock>();
        object[] attributes = Page.GetType().GetCustomAttributes(true);

        foreach (Attribute attribute in attributes)
        {
          if (!(attribute is CodeViewerReferenceAttribute)) continue;

          CodeViewerReferenceAttribute referenceAttribute = (CodeViewerReferenceAttribute) attribute;
          CodeBlock codeBlock = new CodeBlock(referenceAttribute);
          codeBlocks.Add(referenceAttribute.Order, codeBlock);
        }

        _loadedBlocks[Page.GetType()] = codeBlocks;
      }

      return _loadedBlocks[Page.GetType()];
    }    
  }
}